#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File    :   adv_function.py
@Time    :   2025/01/06 20:07:59
@Author  :   xiangyu Chen
@Version :   1.0
@Contact :   yu112yu@sina.com
@License :   (C)Copyright 2017-2018, Liugroup-NLPR-CASIA
@python_requires    :   None
@Desc    :   函数式编程
'''

# 高阶函数


from functools import reduce
from operator import add


def my_add(x, y, f):  # 函数可以接收另一个函数'f'作为参数
    return f(x)+f(y)


print('my_add(-5,6,abs):', my_add(-5, 6, abs))

# Python 内建的 map()和 reduce()函数。


def f(x):
    return x**2


# ============================map 将传入的函数依次作用到序列的每个元素，并把结果作为新的Iterator 返回。
r = map(f, [x for x in range(1, 11) if x % 2 == 0])
# 由于结果 r 是一个Iterator，Iterator 是惰性序列，因此通过 list()函数让它把整个序列都计算出来并返回一个 list。
print(list(r))

# =============================reduce 把一个函数作用在一个序列[x1, x2, x3, ...]上，这个函数必须接收两个参数，reduce 把结果和序列的下一个元素再做为两个参数传入。
print(reduce(add, [x for x in range(1, 11)]))


def f_str(s):
    return s[0:1].upper()+s[1:].lower()
    # print(str.capitalize())     # 把第一个字母转化为大写字母，其余小写


L1 = ['adam', 'LISA', 'barT']
L2 = list(map(f_str, L1))
print(L2)

# 利用 reduce()求list的积


def prod(x):
    return reduce(lambda a, b: a*b, x)


print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))

# 利用 map 和 reduce 编写一个 str2float 函数，把字符串'123.456'转换成浮点数 123.456


def str2float(str):
    def char2num(s):
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

    def str2int(s):
        return reduce(lambda x, y: x*10+y, map(char2num, s))

    def str2float(s):
        n = s.index('.')
        return str2int(s[:n]) + str2int(s[n+1:]) / (10**len(s[n+1:]))
    return str2float(str)


print('str2float(\'123.456\') =', str2float('123.456'))

# ============================Python 内建的 filter()函数用于过滤序列。
# 返回的是一个 Iterator，也就是一个惰性序列，所以要强迫 filter()完成计算结果，需要用 list()函数获得所有结果并返回 list。
# filter()把传入的函数依次作用于每个元素，然后根据返回值是 True 还是 False 决定保留还是丢弃该元素。


def is_odd(n):
    return n % 2 == 1


print(list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])))


# 利用 filter()求素数
def _odd_iter():
    n = 1
    while True:
        n += 2
        yield n


def _not_divisible(n):
    return lambda x: x % n > 0


def primes():
    yield 2
    it = _odd_iter()  # 初始序列
    while True:
        n = next(it)  # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it)  # 构造新序列


# 打印500内的素数
for n in primes():
    if n < 10:
        print(n)
    else:
        break

# 利用 filter()滤掉非回数。


def is_palindrome(n):
    return str(n) == str(n)[::-1]


output = filter(is_palindrome, range(1, 1000))
print(list(output))  # 由于 filter()使用了惰性计算，所以只有在取 filter()结果的时候，才会真正筛选并每次返回下一个筛出的元素。


# ============================ sorted  排序算法

print('正序sorted([36, 5, -12, 9, -21]): ', sorted([36, 5, -12, 9, -21]))  # 正序
print('逆序sorted([36, 5, -12, 9, -21], reverse=True): ',
      sorted([36, 5, -12, 9, -21], reverse=True))  # 逆序
print('使用key函数处理后再排序，按原始元素返回，sorted([36, 5, -12, 9, -21], key=abs) ： ',
      sorted([36, 5, -12, 9, -21], key=abs))
print('忽略字符串大小写排序  sorted([\'bob\', \'about\', \'Zoo\', \'Credit\'], key=str.lower)： ', sorted(
    ['bob', 'about', 'Zoo', 'Credit'], key=str.lower))

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
print('原始名单： ', L)


def by_name(t):
    return t[0]


print('按名字排序：', sorted(L, key=by_name))


def by_score(t):
    return t[1]


print('按分数排序(逆序为从大到小)：', sorted(L, key=by_score, reverse=True))

# ===============================闭包


def count_error():
    fs = []
    for i in range(1, 4):
        def f():
            return i*i
        fs.append(f)  # 此时f未执行，最后f1()时才执行，执行时，for循环已经完成，使用的i已成为3
    return fs  # fs在返回时包含三个函数f，返回闭包时牢记的一点就是：返回函数不要引用任何循环变量，或者后续会发生变化的变量。


f1, f2, f3 = count_error()

print('fs中添加的是函数，f1()时才执行：', f1(), f2(), f3())  # 9，9，9


def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i*i
        fs.append(f())  # 改为直接执行，每次循环时即将f()的结果添加入fs，而不是函数。
    return fs


ff1, ff2, ff3 = count()

print('fs中添加的是函数的执行结果，此时不用再调用：', ff1, ff2, ff3)  # 1，4，9


def count_fun():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))  # f(i)立刻被执行，因此 i 的当前值被传入 f()。此时fs中存入的函数g虽然未执行，但变量值已确定
    return fs


fff1, fff2, fff3 = count_fun()

print('fs中添加的是函数，参数在添加时已明确：', fff1(), fff2(), fff3())  # 1，4，9
